package testredis;

import com.alibaba.rocketmq.shade.com.alibaba.fastjson.JSON;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Response;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.exceptions.JedisDataException;

import java.io.IOException;
import java.util.*;

/**
 * LPOP操作时发生网络中断，但是redis服务器确已经删除成功，返回的值未获取到
 * 相当于造成数据丢失
 *
 * testredis.TestLpopNetError <br>
 *
 * @author 蒋カイセキ    Japan-Tokyo  2018-08-03 16:07:52<br>
 * ブログ http://shoukaiseki.blog.163.com/<br>
 * E-メール jiang28555@Gmail.com<br>
 **/

public class TestLpopNetError {

    static final String listkey = "mylist";
    static final String dual = "dual";
    Jedis jedis = new Jedis("localhost");

    //本地删除redis缓存key队列,最终通过定时任务去处理这些因网络故障导致的异常信息
    List<String> delTmpkey=new ArrayList<>();

    //本地处理redis缓存key队列,最终通过定时任务去处理这些因网络故障导致的异常信息
    Map<String,String> cache=new HashMap<>();

    public static void main(String[] args) {
        TestLpopNetError test=new TestLpopNetError();
        test.initdata();
        test.test1();
    }

    public void initdata(){
        jedis.del(listkey);
        //这个key用于事务级别查询时判断其它语句是否执行成功
        jedis.set(dual,"1");
        jedis.rpush("mylist","one");
        jedis.rpush("mylist","two");
        jedis.rpush("mylist","three");

    }

    public  void test1() {
        //本次缓存
        String tmpkey = UUID.randomUUID().toString();
        System.out.println(tmpkey);

        //错误次数计数
        int errorcount=0;
        String lindexValue=null;


        //jedis 的网络故障异常需要使用try拦截,这里就不写了
        while (true){
            Transaction multi = jedis.multi();
//            Response<String> info = multi.info("cpu");
            //使用info("cpu")传输数据过多,建立一个专用查询key存放
            Response<String> info =multi.get(dual);
            Response<String> tmpvalue = multi.get(tmpkey);

            multi.exec();
            try {
                multi.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

            //获取缓存key的值
            //为保证
            String tmpvalueStr = tmpvalue.get();
            String infoStr = info.get();
            System.out.println("tmpValueStr="+tmpvalueStr);
            if(infoStr !=null){
                if(tmpvalueStr!=null){
                    System.out.println("tmpvalue is not null");
                    action(lindexValue,tmpkey);
                    break;

                }else{
                    jedis.watch(listkey);
                    String lindexTmp = jedis.lindex(listkey, 0);
                    //list中无可用元素，直接退出
                    if(lindexTmp==null){
                        break;
                    }
                    lindexValue=lindexTmp;
                    multi=jedis.multi();
                    multi.lpop(listkey);
                    multi.set(tmpkey,lindexTmp);
                    info =multi.get(dual);


                    List<Object> exec = multi.exec();
                    System.out.println(JSON.toJSONString(exec));
                    if(exec.size()!=0&&info.get()!=null){
                        //成功执行完毕，则处理业务
                        action(lindexValue,tmpkey);
                        break;
                    }
                }

            }else{
                //网络访问异常出错次数超出则推出循环
                if(++errorcount>10){
                    if(lindexValue!=null){
                        //存放到本地处理缓存，网络故障
                       cache.put(tmpkey,lindexValue);
                    }
                   break;
                }

            }
        }
    }

    /**
     * 处理业务
     * @param value  业务值
     * @param tmpkey 缓存key
     */
    public void action(String value,String tmpkey){
        System.out.println("value="+value);
        //删除缓存key
        Long del = jedis.del(tmpkey);
        System.out.println("del="+del);
        if(del!=null&&del!=1){
            //删除缓存key时断网获失败，则加入到本地缓存队列中
            // 由定时任务去删除额外的缓存数据,保证redis垃圾数据及时清理
            //这里只是放到list做个演示
            delTmpkey.add(tmpkey);
        }
    }

}
